package io.mft.runtime.config.impl;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import cn.hutool.core.util.XmlUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.mft.api.domain.Api;
import io.mft.api.domain.ApiFunctionType;
import io.mft.api.domain.ApiRegulation;
import io.mft.api.domain.DataAccessRegulation;
import io.mft.api.domain.builder.SqlIdBuilder;
import io.mft.commons.config.ConfigProvider;
import io.mft.commons.exception.MFTException;
import io.mft.commons.function.datasource.DatasourceInvokeType;
import io.mft.runtime.config.ApiLoader;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ApplicationConfigApiLoader extends ApiLoader {

	static boolean warn=false;

	@Override
	protected List<Api> loadApi(String appid) {
		
		String apiConfigNamespace="api";//应用接口配置
		List<ClassLoader> classLoaders=ConfigProvider.getConfigClassLoaders();
		Map<String, Api> apiMap=new HashMap<String, Api>();
		Map<String, ClassLoader> apiClassLoaderMap=new HashMap<String, ClassLoader>();
		classLoaders.forEach(classLoader->{
			try {
				Map<String, Object> apiConfigMap=ConfigProvider.getNamespaceConfigObject(classLoader, apiConfigNamespace);
				if (apiConfigMap==null) {
					if (!warn) {
						log.warn("应用插件["+classLoader.getResource(".").getPath()+"]接口目录未配置接口,请检查是否忽略!!!!!!!!!!!!!!!");
						warn=true;
					}
					
				}else {
					Set<String> apiKeys=apiConfigMap.keySet();
					apiKeys.forEach(apiKey->{
						try {
							String v=String.valueOf(apiConfigMap.get(apiKey));
							Api api=parseApi(classLoader,apiKey, v);
							if (apiMap.containsKey(apiKey)) {
								StringBuilder msg=new StringBuilder();
								msg.append("当前接口["+apiKey+"]归属应用目录["+classLoader.getResource(".").getPath()+"]系统["+api.getModule().getSystem().getKey()+"]模块["+api.getModule().getAliasKey()+"]");
								msg.append("存在于应用目录["+apiClassLoaderMap.get(apiKey).getResource(".").getPath()+"]系统["+apiMap.get(apiKey).getModule().getSystem().getKey()+"]模块["+apiMap.get(apiKey).getModule().getAliasKey()+"]");
								throw new MFTException(msg.toString());
							}
							apiMap.put(apiKey, api);
							apiClassLoaderMap.put(apiKey, classLoader);
						} catch (MFTException e) {
							log.error("",e);
						}catch (Exception e) {
							log.error("应用插件["+classLoader.getResource(".").getPath()+"]api["+apiKey+"]元数据解析异常",e);
						}
					});				
				}				
			} catch (Exception e2) {
				log.error("应用插件["+classLoader.getResource(".").getPath()+"]接口解析异常",e2);
			}
		});
		List<Api> apis=new ArrayList<Api>(apiMap.values());
		return apis;
	}

	private Api parseApi(ClassLoader pluginClassLoader,String key,String v) {
		JSONObject jsonObject=JSONUtil.parseObj(v);
		if (jsonObject.isEmpty()) {
			throw new MFTException(-1,"api["+key+"]元数据json是空,请检查配置");
		}
		Api api=parseApi(pluginClassLoader,jsonObject);
		if (!api.getKey().equals(key)) {
			throw new MFTException("接口号["+api.getKey()+"]与接口元数据名称不一致");
		}
		return api;
	}
	@Override
	protected void processApiRegulation(ClassLoader pluginClassLoader,Api api, ApiRegulation apiRegulation) {
		if (api.getApiFunctionType()==ApiFunctionType.processExecutor) {
			try {
				if (apiRegulation.getRegulation() instanceof DataAccessRegulation) {
					DataAccessRegulation dataAccessRegulation=(DataAccessRegulation)apiRegulation.getRegulation();
					if (dataAccessRegulation.getType()==DatasourceInvokeType.sql||dataAccessRegulation.getType()==DatasourceInvokeType.sqlfunction||dataAccessRegulation.getType()==DatasourceInvokeType.sqlprocedure) {
						String value=dataAccessRegulation.getValue();
						String xml="sql.xml";
						if (value.equals(xml)) {
							String sqlXmlNamespace="apiSQL";
							Map<String, Object> map=ConfigProvider.getNamespaceConfigObject(pluginClassLoader,sqlXmlNamespace);
							String mapperXml=String.valueOf(map.get(api.getKey()));
							DocumentBuilderFactory factory=XmlUtil.createDocumentBuilderFactory();
							factory.setFeature("http://xml.org/sax/features/external-general-entities",false);
							factory.setFeature("http://xml.org/sax/features/external-parameter-entities",false);
							factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl",false);
							InputStream   inputStream   =   new   ByteArrayInputStream(mapperXml.getBytes());
							Document document=factory.newDocumentBuilder().parse(inputStream);
							NodeList nodeList=XmlUtil.getNodeListByXPath("mapper/select|mapper/update|mapper/delete|mapper/insert|mapper/sql", document);
							Node node=nodeList.item(0);
							if (node.getNodeType() == Element.ELEMENT_NODE)
							{
								Element e = (Element) node;
								String id= e.getAttribute("id");
								String sqlId=SqlIdBuilder.build(id, dataAccessRegulation.getPos());
								e.setAttribute("id", sqlId);
							}
							mapperXml=XmlUtil.toStr(document);
							dataAccessRegulation.setValue(mapperXml);
				 		}else {
				 			String sqlId=SqlIdBuilder.build(api, dataAccessRegulation);
				 			String mapperXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE mapper  PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\"> <mapper> <sql id=\""
				 					+ sqlId + "\"> " + value + "</sql></mapper>";
				 			dataAccessRegulation.setValue(mapperXml);
						}
					}				
				}
			} catch (Exception e) {
				log.error("",e);
			}
		}
	}
}
